什么是响应式 Web 设计?怎样进行?

Info

开始第一篇。老规矩,先无聊的谈论天气一类的话题。十一长假,天气也终于开始有些秋天的味道,坐在屋里甚至觉得需要热咖啡。话说两年前也是在国庆假期里开始做Joomla文档翻译的;长假好时光,总会可以抽出一两天,安静的窝在家里做做博客、学做些新东西,简直没有比这更舒心的事情。

说正事儿。准备在近期的几篇里集中翻译学习一下“响应式Web设计”的相关话题,包括概念、实践方式、案例及观点讨论等方面。相比于从前做的文档译文,这些文章篇幅要长的多(甚至要加分页了!...),今天放上的这篇几乎花掉了两天的“闲暇时间”;对耐力是个考验,努力提高喽。废话结束,here we go.


眼下,几乎每个新客户都希望他们的网站可以有专门的移动设备版本。最完美的情况呐,就是为iPhone、iPad、黑莓、Kindle...各自打造一款——页面分辨率还必须兼容任何设备。谁知道未来5年内我们还需要为多少新发明的设备设计开发不同版本的页面?这种疯狂什么时候算个头?

在Web设计和开发领域,很快的,我们将会无法跟上设备与分辨率革新的步伐。对于多数网站来说,为每种新设备及分辨率创建其独立的版本根本就是不切实际的;结果就是,我们将会赢得使用某些设备的用户群,而失去那些使用其他设备的用户。不过,或许会有另外一种方式,可以帮助我们避免这种情况的发生。

响应式Web设计(Responsive Web design)的理念是,页面的设计与开发应当根据用户行为以及设备环境(系统平台、屏幕尺寸、屏幕定向等)进行相应的响应和调整。具体的实践方式由多方面组成,包括弹性网格和布局、图片、CSS media query的使用等。无论用户正在使用笔记本还是iPad,我们的页面都应该能够自动切换分辨率、图片尺寸及相关脚本功能等,以适应不同设备;换句话说,页面应该有能力去自动响应用户的设备环境。这样,我们就可以不必为不断到来的新设备做专门的版本设计和开发了。

响应式 Web 设计的概念

Ethan Marcotte曾经在A List Apart发表过一篇文章“Responsive Web Design”,文中援引了响应式建筑设计的概念:

最近出现了一门新兴的学科——“响应式建筑(responsive architecture)”——提出,物理空间应该可以根据存在于其中的人的情况进行响应。结合嵌入式机器人技术以及可拉伸材料的应用,建筑师们正在尝试建造一种可以根据周围人群的情况进行弯曲、伸缩和扩展的墙体结构;还可以使用运动传感器配合气候控制系统,调整室内的温度及环境光。已经有公司在生产“智能玻璃”:当室内人数达到一定的阀值时,这种玻璃可以自动变为不透明,确保隐私。

将这个思路延伸到Web设计的领域,我们就得到了一个全新的概念。为什么一定要为每个用户群各自打造一套设计和开发方案?和响应式建筑相似,Web设计同样应该做到根据不同设备环境自动响应及调整。

显然,我们无法也无需使用运动传感器或是机器人技术,响应式Web设计更多需要的是抽象思维。好在,一些相关的概念已经得到了实践,比如液态布局、帮助页面重新格式化的media queries和脚本等。但是响应式Web设计不仅仅是关于屏幕分辨率自适应以及自动缩放的图片等等,它更像是一种对于设计的全新思维模式。

调整分辨率

不同的设备都有各自的屏幕分辨率、清晰度以及屏幕定向方式,不断被研发着的各种新设备也将带来新的屏幕尺寸规格。有些设备基于竖屏(portrait),有些是横屏(landscape),甚至还有正方形;对于日益流行的iPhone、iPad及其他一些智能手机、平板电脑,用户还可以通过转动设备来任意切换屏幕的定向方式。怎样才能做到让一种设计方案满足所有情况?

Beforweb/2011/images/cbff3ab53cda128130dd79fe3d15231d_MD5.jpg

要想做到同时兼容横、竖屏(用户还有可能在页面加载的过程中切换方向),我们就必须考虑N种屏幕尺寸规格。诚然,我们可以将这些规格划分为几个大类,然后为每一类做一种方案,确保该方案至少在本组中尽量具有弹性。但即使这样,结果也将是无比焦虑的,谁知道某类设备在5年之后的占有率是多少?而且很多用户甚至不去将浏览器的窗口最大化;类似这样的变数,我们还要考虑多少呢?

Morten Hjerde和他的同事们对2005至2008年市场中的400余种移动设备进行了统计(查看报告),下图展示了大致的统计结果:

Beforweb/2011/images/b2077ff4e9f9a36a64d95ed64b050730_MD5.jpg

在08年之后,更多更有代表性的新设备问世并普及了。显然,我们不可以沿着“多方案”的思路继续走下去;那么我们应该怎样做呢?

部分解决方案:一切弹性化

几年前,弹性布局(flexible layout)几乎是一种奢侈品,所谓弹性,也只是体现在竖排布局以及字号等方面;图片始终可以轻易的破坏页面结构,而且即使是哪些弹性的元素结构,在很极端的情况下,仍会破坏布局。所以,所谓的弹性布局其实并非那样弹性,它有时甚至不能适应台式机与笔记本的屏幕分辨率差异,更不用说手机等移动设备了。

现在,我们可以通过响应式的设计和开发思路让页面更加“弹性”了。图片的尺寸可以被自动调整,页面布局再不会被破坏。虽然永远没有最完美的解决方案,但它给了我们更多选择。无论用户切换设备的屏幕定向方式,还是从台式机屏幕转到iPad上浏览,页面都会真正的富有弹性。

在前文提到的Ethan Marcotte的文章中,他通过一个实例展示了响应式Web设计在页面弹性方面的特性:

Beforweb/2011/images/0f49ca34807730722486328361af8e51_MD5.jpg

该实例的实现方式完美的结合了液态网格液态图片技术,并且聪明的在正确的地方使用了正确的HTML标记。“液态网格”是一种很常见的实践方式;对于“液态图片”技术,下面的文章做了不错的介绍:

说到创建液态页面,最好看看Zoe Mickley Gillenwater的那本《Flexible Web Design: Creating Liquid and Elastic Layouts with CSS》,或是下载个样章先:“怎样创建弹性图片”。另外,Zoe的这篇“Essential Resources for Creating Liquid and Elastic Layouts.”提供了不少关于创建弹性网格和布局的教程、资源、创意指导和最佳实践方式。

从技术角度讲,虽然听上去这些都简单可行,但它比“将这些功能结合在一起”要复杂些。举个例子,仔细观察Ethan Marcotte提供的实例中的logo图片:

Beforweb/2011/images/ed95a0305ea87e44ca57e118e2d5e174_MD5.jpg

如果我们将浏览器窗口不断调小,会发现logo图片的文字部分始终会保持同比缩小,保证其完整可读,而不会和周围的插图一样被两边裁掉。所以整个logo其实包括两部分:插图作为页面标题的背景图片,会保持尺寸,但会随着布局调整而被裁切;文字部分则是一张单独的图片。

<h1 id = "logo">
  <a href = "#"><img src = "site/logo.png" alt = "The Baker Street Inquirer" /></a>
</h1>

其中,<h1>元素使用插图作为背景,文字部分的图片始终保持与背景的对齐。

这个实例小小的展示一下响应式Web设计的思路。不过这点小努力还不足以避免整个布局在小尺寸窗口中变的过窄或过短,并且logo文字最终会变的小到难以识别,背景图片也会被过多的裁切。

弹性图片

响应式Web设计的思路中,一个重要的因素是怎样处理图片方面的问题。有很多同比缩放图片的技术,其中有不少是简单易行的。其中,由Richard Rutter最先尝试的一种做法比较流行,即使用CSS的max-width属性。这个方法在Ethan Marcotte的液态图片一文中也有提到。

img { max-width: 100%;}

只要没有其他涉及到图片宽度的样式代码覆盖掉这一行规则,页面上所有的图片就会以其原始宽度进行加载,除非其容器可视部分的宽度小于图片的原始宽度。上面的代码确保图片最大的宽度不会超过浏览器窗口或是其容器可视部分的宽度,所以当窗口或容器的可视部分开始变窄时,图片的最大宽度值也会相应的减小,图片本身永远不会容器边缘隐藏和覆盖。实际上,就像Jason Grigsby在他的CSS Media Query for Mobile is Fool’s Gold一文中提到的,“液态图片背后的思路,就是无论何时,都确保在其原始宽度范围内,以最大的宽度同比完整的显示图片。我们不必在样式表中为图片设置宽度和高度,只需要让样式表在窗口尺寸发生变化时辅助浏览器对图片进行缩放。” 一种简而美的方法。

图片本身的分辨率及加载时间是另外一个需要考虑的问题。虽然通过上面的方法,可以很轻松的缩放图片,确保在移动设备的窗口中可以被完整浏览,但如果原始图片本身过大,便会显著降低图片文件的下载速度,对存储空间也会造成没有必要的消耗。

响应式图片

由Filament Group提出的“响应式图片”技术思想,有助于解决上面提到的问题:不仅要同比的缩放图片,还要在小设备上降低图片自身的分辨率。可以看下demo页面先。

Beforweb/2011/images/b6048e2c674dc4f1926c81f4b7cabcfb_MD5.jpg

这个技术的实现需要使用几个相关文件,我们可以在Github上获取。包括一个JavaScript文件(rwd-images.js),一个.htaccess文件,以及一些范例资源文件。具体使用方法可以参考Responsive Images的说明文档。大致的原理是,rwd-images.js会检测当前设备的屏幕分辨率,如果是大屏幕设备,则向页面head部分中添加BASE标记,并将后续的图片、脚本和样式表加载请求定向到一个虚拟路径“/rwd-router”。当这些请求到达服务器端,.htacces文件会决定这些请求所需要的是原始图片还是小尺寸的“响应式图片”,并进行相应的反馈输出。对于小屏幕的移动设备,原始尺寸的大图片永远不会被用到。

这项技术支持多数的现代浏览器,包括IE8+、Safari、Chrome和Opera,以及这些浏览器的移动设备版本;在FireFox及一些旧浏览器中,则会优雅降级:我们仍可得到小图片的输出,但同时,原始大图也会被下载。

禁用 iPhone 中的图片自动缩放

在iPhone及iPod Touch中,页面会被自动的同比例缩小至最适合屏幕大小的尺寸,x轴不会产生滚动条,用户可以上下拖拽浏览全部页面,或在需要的时候放大页面的局部。这里会产生一个问题,即使我们运用响应式Web设计的思想,专门为iPhone的小屏输出小图片,它同样会随着整个页面一起被同比例缩小,如下图左侧所示。

Beforweb/2011/images/107cfd5f9d1c76b560f796d4eb68ba98_MD5.jpg

我们可以使用苹果专有的一些meta标记来解决类似的问题。在页面的<head>部分添加以下代码(详情可以参考Think Vitamin的相关文章):

<meta name = "viewport" content = "width=device-width; initial-scale=1.0">

将initial-scale的值设定为“1”,即可覆写默认的缩放方式,保持原始的尺寸及比例。更多关于viewport meta标记的用法,可以参考苹果官方的文档

打造布局结构

当页面所需要适应的不同设备的屏幕尺寸差异过大时,除了图片方面,我们也应该考虑到整个布局结构的响应式调整;我们可以使用独立的样式表,或者更有效的,使用CSS media query。这不会引起多大的麻烦,多数样式设定不会被影响和改变,只有一些特定的元素会通过浮动、宽度和高度等的设置来改变位置。

我们可以使用一个默认主样式表来定义页面的主要结构元素,比如#wrapper、#content、#sidebar、#nav等的默认布局方式,以及一些全局性的配色和字体方案。

我们可以监测页面布局随着不同的浏览环境而产生的变化,如果它们变的过窄过短或是过宽过长,则通过一个子级样式表来继承主样式表的设定,并专门针对某些布局结构进行样式覆写。我们来看下代码示例:

下面的代码可以放在默认主样式表style.css中:

/* Default styles that will carry to the child style sheet */
 
html,body{
   background...
   font...
   color...
}
 
h1,h2,h3{}
p, blockquote, pre, code, ol, ul{}
 
/* Structural elements */
#wrapper{
    width: 80%;
    margin: 0 auto;
 
    background: #fff;
    padding: 20px;
}
 
#content{
    width: 54%;
    float: left;
    margin-right: 3%;
}
 
#sidebar-left{
    width: 20%;
    float: left;
    margin-right: 3%;
}
 
#sidebar-right{
    width: 20%;
    float: left;
}

下面的代码可以放在子级样式表mobile.css中,专门针对移动设备进行样式覆写:

#wrapper{
    width: 90%;
}
 
#content{
    width: 100%;
}
 
#sidebar-left{
    width: 100%;
    clear: both;
 
    /* Additional styling for our new layout */
    border-top: 1px solid #ccc;
    margin-top: 20px;
}
 
#sidebar-right{
    width: 100%;
    clear: both;
 
    /* Additional styling for our new layout */
    border-top: 1px solid #ccc;
    margin-top: 20px;
}

大致的视觉效果如下图所示:

Beforweb/2011/images/836be001a19c18bc48cc3bc6c0aae032_MD5.jpg

Media Queries

CSS3支持CSS2.1所支持的所有媒体类型,例如screen、print、handheld等,同时添加了很多涉及媒体类型的功能属性,包括max-width(最大宽度)、device-width(设备宽度)、orientation(屏幕定向,横屏或竖屏)和color。在CSS3发布之后出现的新玩具,如iPad或Android相关设备,都可以完美的支持这些属性。所以我们可以通过media query为新设备设置独特的样式,而忽略那些不支持CSS3的台式机中的旧浏览器。

在Ethan的文章中,我们能看到一段media query使用实例:

<link rel="stylesheet" type="text/css"
    media="screen and (max-device-width: 480px)"
    href="shetland.css" />

代码本身可以很好的说明工作机制:如果页面通过屏幕呈现(非打印一类),并且屏幕宽度不超过480px,则加载shetland.css样式表。要了解更多关于CSS3媒体新属性的信息,可以参考“The Orientation Media Query”一文。

我们可以创建多个样式表,以适应不同设备类型的宽度范围。Ethan的文章中的“Meet the media query”部分有更多的范例及解释。更有效率的做法是,将多个media queries整合在一个样式表文件中:

/* Smartphones (portrait and landscape) ----------- */
@media only screen
and (min-device-width : 320px)
and (max-device-width : 480px) {
/* Styles */
}
 
/* Smartphones (landscape) ----------- */
@media only screen
and (min-width : 321px) {
/* Styles */
}
 
/* Smartphones (portrait) ----------- */
@media only screen
and (max-width : 320px) {
/* Styles */
}

上面的代码来自于Andy Clark创建的可以兼容各种主流设备的免费模板。这样整合多个media queries于一个样式表文件的方式,与通过media queries调用不同样式表的方式之间的区别与联系,可以参考“Hardboiled CSS3 Media Queries”一文。

CSS3 Media Queries

上面演示的一些代码范例是CSS2.1与CSS3通吃的。现在让我们来看看怎样使用CSS3专有的media queries功能来创建响应式Web设计。其中某些方法在当前就有切实的使用价值,不久的将来则一定会全部派上用场。

min-widthmax-width这两个属性很靠谱。通过min-width的设置,我们可以在浏览器窗口或设备屏幕宽度高于这个值的情况下,为页面指定一个特定的样式表;max-width则反之。

下面的几个示例中,我们使用的是将多个media queries整合在单一样式表中进行编码的句法。如前文所述,这样做更加高效,减少请求数量。

@media screen and (min-width: 600px) {
     .hereIsMyClass {
          width: 30%;
          float: right;
     }
}

上面代码中定义的类(hereIsMyClass)只有在浏览器或屏幕宽度超过600px时才会有效。

@media screen and (max-width: 600px) {
     .aClassforSmallScreens {
          clear: both;
          font-size: 1.3em;
     }
}

而这段代码的作用则相反:aClassforSmallScreens类只有在浏览器或屏幕宽度小于600px时才会有效。

可以看出min-width和max-width可以同时判断设备屏幕尺寸与浏览器实际宽度。有些时候,我们希望通过media queries作用于某种特定的设备,而忽略其上运行的浏览器是否由于没有最大化而在尺寸上与设备屏幕尺寸产生不一致的情况。这时,我们需要使用min-device-widthmax-device-width,用来判断设备本身的屏幕尺寸。

@media screen and (max-device-width: 480px) {
     .classForiPhoneDisplay {
          font-size: 1.2em;
     }
}
@media screen and (min-device-width: 768px) {
     .minimumiPadWidth {
          clear: both;
          margin-bottom: 2px solid #ccc;
     }
}

还有一些其他方法,可以帮我们有效的使用media queries锁定某些指定的设备。可以参考下面两篇出自Thomas Maier的文章:

对于iPad来说,orientation属性尤其有用。它的值可以是landscape(横屏)或portrait(竖屏)。

@media screen and (orientation: landscape) {
     .iPadLandscape {
          width: 30%;
          float: right;
     }
}
@media screen and (orientation: portrait) {
     .iPadPortrait {
          clear: both;
     }
}

不幸的是,这个属性目前确实只在iPad上有效。对于其他可以转屏的设备,譬如iPhone,可以使用min-device-width和max-device-width来变通实现;详情可以参考“Determine iPhone orientation using CSS”一文。

我们还可以将上述属性组合使用,来锁定某个屏幕尺寸范围:

@media screen and (min-width: 800px) and (max-width: 1200px) {
     .classForaMediumScreen {
          background: #cc0000;
          width: 30%;
          float: right;
     }
}

上面的代码可以作用于浏览器窗口或屏幕宽度在800px至1200px之间的所有设备。

其实,很多设计师和开发者仍会选择使用多个样式表的方式来实现media queries。如果从资源的组织和维护的角度出发,其益处确实高于效率的损耗的话,那么这样做也完全不坏:

<link rel="stylesheet" media="screen and (max-width: 600px)" href="small.css" />
<link rel="stylesheet" media="screen and (min-width: 600px)" href="large.css" />
<link rel="stylesheet" media="print" href="print.css" />

所以呐,凡事没有绝对,最好根据实际情况决定使用media queries的方式。比如,对于iPad,我们可以将多个media queries直接写在一个样式表中。因为iPad用户随时有可能切换屏幕定向,这种情况下,要保证页面在极短的时间内响应屏幕尺寸的调整,我们必须选择效率最高的方式。

JavaScript

JavaScript也是我们的武器之一,特别是当某些旧设备无法完美支持CSS3的media query时,它可以作为后备支援。幸运的是,已经有专门的JS库来帮助旧浏览器(IE 5+,Firefox 1+,Safari 2等)支持CSS3的media queries。使用方法很简单,下载css3-mediaqueries.js并在你的页面中调用它。

而下面的代码则演示了怎样使用简单的几行jQuery代码来检测浏览器宽度,并为不同的情况调用不同的样式表:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
 
<script type="text/javascript">
    $(document).ready(function(){
        $(window).bind("resize", resizeWindow);
        function resizeWindow(e){
            var newWindowWidth = $(window).width();
 
            // If width width is below 600px, switch to the mobile stylesheet
            if(newWindowWidth < 600){                $("link[rel=stylesheet]").attr({href : "mobile.css"});              }           // Else if width is above 600px, switch to the large stylesheet             else if(newWindowWidth > 600){
                $("link[rel=stylesheet]").attr({href : "style.css"});
            }
        }
    });
</script>

类似这样的解决方案还有很多。所以我们要清楚,media queries不是一个绝对唯一的答案,它只是一个以纯CSS方式实现响应式Web设计思路的手段。借助JavaScript,我们则可以实现更多的变化。这篇“Combining Media Queries and JavaScript”向我们展示了JavaScript配合media queries的更多细节信息。

显示或隐藏内容

通过前文的学习,我们已经了解到,对于响应式Web设计,同比例缩减元素尺寸以及调整页面结构布局,是两个重要的方式方法。但是对于页面中的文字内容信息来说,则不能简单的只从“同比缩小”和“调整布局结构”这两方面去处理。对于手机等移动设备来说,在文字内容方面,已经有了很多最佳实践方式和指导原则:简化的导航、更易聚焦的内容、以信息列表代替传统的多行文案内容等。

Beforweb/2011/images/d1fbd67d95a242f41232ceba7155551b_MD5.jpg

响应式Web设计的思想,一方面要保证页面元素及布局具有足够的弹性,来兼容各类设备平台和屏幕尺寸;另一方面,则是增强可读性和易用性,帮助用户在任何设备环境中都能更容易的获取最重要的内容信息。

有一条样式代码,我们已经使用了多年:

display: none;

我们可以在一个针对某类小屏幕设备的样式表中使用它来隐藏掉页面中的某些块级元素,也可以使用前文的方法,通过JS判断当前硬件屏幕规格,在小屏幕设备的情况下直接为需要隐藏的元素添加工具类class。比如,对于手机类设备,我们可以隐藏掉大块的文字内容区,而只显示一个简单的导航结构,其中的导航元素可以指向详细内容页面。

注意,不要使用visibility: hidden的方式,因为这只能使元素在视觉上不做呈现;display属性则可帮助我们设置整块内容是否需要被输出。对于移动设备来说,避免这些不必要的资源浪费还是很重要的。我们来看一个简单的示例:

Beforweb/2011/images/afbba84d5557dc5b26567448d18b7784_MD5.jpg

图中上半部分是大屏幕设备所显示的完整页面,下面的则是该页面在小屏幕设备的呈现方式。页面HTML代码如下:

<p class="sidebar-nav"><a href="#">Left Sidebar Content</a> | <a href="#">Right Sidebar Content</a></p>
 
<div id="content">
    <h2>Main Content</h2>
</div>
 
<div id="sidebar-left">
    <h2>A Left Sidebar</h2>
 
</div>
 
<div id="sidebar-right">
    <h2>A Right Sidebar</h2>
</div>

下面是默认的主样式表,其中,我们要隐藏掉链接导航部分(sidebar-nav),因为默认样式适用的设备屏幕会足够大,足够显示包括两个侧边栏在内的所有内容。

#content{
    width: 54%;
    float: left;
    margin-right: 3%;
}
 
#sidebar-left{
    width: 20%;
    float: left;
    margin-right: 3%;
}
 
#sidebar-right{
    width: 20%;
    float: left;
}
.sidebar-nav{display: none;}

下面是用于小屏幕移动设备的样式表代码。现在,我们要隐藏掉两个侧边栏,并使sidebar-nav显示出来。借助JavaScript,当用户点击sidebar-nav中的链接时,对应的侧边栏可以恢复显示。当然,触发恢复显示的方式有很多种,即可以通过JS改变侧边栏的display属性值,也可以为其添加额外的布局样式。

#content{
    width: 100%;
}
 
#sidebar-left{
    display: none;
}
 
#sidebar-right{
    display: none;
}
.sidebar-nav{display: inline;}

现在,我们的页面已经可以随着设备和屏幕规格的变更,响应式的做到元素的同比缩放、布局结构的改变、内容的优化调整。特别是对于手机设备,我们还要在实践过程中注意一些该类设备共有的设计指导原则。比如,针对手机设备,使用一个特定的样式,将页面原有的文字导航元素变为更易操作的图标形式。下面的一些文章资源可作参考阅读:

触屏与鼠标

触屏设备已经成为主流。虽然目前多数触屏设备还是小屏幕类型的产品,比如手机,但是市场上越来越多的大屏幕设备也开始使用触屏技术;且不说iPad一类的平板电脑,就连一些笔记本和台式机也加入了这一行列。比如HP Touchsmart tm2t,即使用传统的键鼠设备,同时也加入了触屏技术。

Beforweb/2011/images/7a17fd12e6e548d2a22df74bafbf5822_MD5.jpg

相比于传统的基于鼠标指针的互动,触屏技术显然带来了截然不同的交互方式与相应的设计规范;两者又有各自所适用的领域。所幸,要使我们的设计方案同时满足这两类设备的规范,并非一件难事,只是有些地方需要注意。比如,触屏设备无法反映CSS定义的hover行为及相应的样式,因为它没有鼠标指针的概念,手指点击就是click行为。所以不要让任何功能依赖于对hover状态的触发。

有兴趣的话,可以读读这篇“Designing for Touchscreen”,这里提到的很多建议即有利于改进针对触屏设备的设计方式,同时也不会削弱传统键鼠设备上的用户体验。比如,放在页面右侧的导航列表可以对触屏设备的用户更加友好。因为多数人习惯用右手点击操作,而左手负责握住设备;这样,放在右侧的导航列表即方便右手的点击,又可以避免被握着设备的左手不小心触碰到。而这一点与键鼠设备用户的习惯完全不矛盾。

Info
Warning

C7210.com and Beforweb.com by C7210 is licensed under CC BY-NC-ND 4.0. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-nd/4.0/